{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 기본 예시: 프롬프트 + 모델 + 출력 파서\n",
    "\n",
    "가장 기본적이고 일반적인 사용 사례는 prompt 템플릿과 모델을 함께 연결하는 것입니다. 이것이 어떻게 작동하는지 보기 위해, 각 나라별 수도를 물어보는 Chain을 생성해 보겠습니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# API KEY를 환경변수로 관리하기 위한 설정 파일\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "# API KEY 정보로드\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# LangSmith 추적을 설정합니다. https://smith.langchain.com\n",
    "# !pip install -qU langchain-teddynote\n",
    "from langchain_teddynote import logging\n",
    "\n",
    "# 프로젝트 이름을 입력합니다.\n",
    "logging.langsmith(\"CH01-Basic\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 프롬프트 템플릿의 활용\n",
    "\n",
    "`PromptTemplate`\n",
    "\n",
    "- 사용자의 입력 변수를 사용하여 완전한 프롬프트 문자열을 만드는 데 사용되는 템플릿입니다\n",
    "- 사용법\n",
    "  - `template`: 템플릿 문자열입니다. 이 문자열 내에서 중괄호 `{}`는 변수를 나타냅니다.\n",
    "  - `input_variables`: 중괄호 안에 들어갈 변수의 이름을 리스트로 정의합니다.\n",
    "\n",
    "`input_variables`\n",
    "\n",
    "- input_variables는 PromptTemplate에서 사용되는 변수의 이름을 정의하는 리스트입니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_teddynote.messages import stream_response  # 스트리밍 출력\n",
    "from langchain_core.prompts import PromptTemplate"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`from_template()` 메소드를 사용하여 PromptTemplate 객체 생성\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# template 정의\n",
    "template = \"{country}의 수도는 어디인가요?\"\n",
    "\n",
    "# from_template 메소드를 이용하여 PromptTemplate 객체 생성\n",
    "prompt_template = PromptTemplate.from_template(template)\n",
    "prompt_template"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# prompt 생성\n",
    "prompt = prompt_template.format(country=\"대한민국\")\n",
    "prompt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# prompt 생성\n",
    "prompt = prompt_template.format(country=\"미국\")\n",
    "prompt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "model = ChatOpenAI(\n",
    "    model=\"gpt-4.1-nano\",\n",
    "    temperature=0.1,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Chain 생성\n",
    "\n",
    "### LCEL(LangChain Expression Language)\n",
    "\n",
    "![lcel.png](./images/lcel.png)\n",
    "\n",
    "여기서 우리는 LCEL을 사용하여 다양한 구성 요소를 단일 체인으로 결합합니다\n",
    "\n",
    "```\n",
    "chain = prompt | model | output_parser\n",
    "```\n",
    "\n",
    "`|` 기호는 [unix 파이프 연산자](<https://en.wikipedia.org/wiki/Pipeline_(Unix)>)와 유사하며, 서로 다른 구성 요소를 연결하고 한 구성 요소의 출력을 다음 구성 요소의 입력으로 전달합니다.\n",
    "\n",
    "이 체인에서 사용자 입력은 프롬프트 템플릿으로 전달되고, 그런 다음 프롬프트 템플릿 출력은 모델로 전달됩니다. 각 구성 요소를 개별적으로 살펴보면 무슨 일이 일어나고 있는지 이해할 수 있습니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# prompt 를 PromptTemplate 객체로 생성합니다.\n",
    "prompt = PromptTemplate.from_template(\"{topic} 에 대해 쉽게 설명해주세요.\")\n",
    "\n",
    "model = ChatOpenAI(model=\"gpt-4.1-nano\", temperature=0.1)\n",
    "\n",
    "chain = prompt | model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### invoke() 호출\n",
    "\n",
    "- python 딕셔너리 형태로 입력값을 전달합니다.(키: 값)\n",
    "- invoke() 함수 호출 시, 입력값을 전달합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# input 딕셔너리에 주제를 '인공지능 모델의 학습 원리'으로 설정합니다.\n",
    "input = {\"topic\": \"인공지능 모델의 학습 원리\"}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# prompt 객체와 model 객체를 파이프(|) 연산자로 연결하고 invoke 메서드를 사용하여 input을 전달합니다.\n",
    "# 이를 통해 AI 모델이 생성한 메시지를 반환합니다.\n",
    "chain.invoke(input)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "아래는 스트리밍을 출력하는 예시 입니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 스트리밍 출력을 위한 요청\n",
    "answer = chain.stream(input)\n",
    "# 스트리밍 출력\n",
    "stream_response(answer)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 출력파서(Output Parser)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "\n",
    "output_parser = StrOutputParser()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Chain 에 출력파서를 추가합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 프롬프트, 모델, 출력 파서를 연결하여 처리 체인을 구성합니다.\n",
    "chain = prompt | model | output_parser"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# chain 객체의 invoke 메서드를 사용하여 input을 전달합니다.\n",
    "input = {\"topic\": \"인공지능 모델의 학습 원리\"}\n",
    "chain.invoke(input)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 스트리밍 출력을 위한 요청\n",
    "answer = chain.stream(input)\n",
    "# 스트리밍 출력\n",
    "stream_response(answer)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 템플릿을 변경하여 적용\n",
    "\n",
    "- 아래의 프롬프트 내용을 얼마든지 **변경** 하여 테스트 해볼 수 있습니다.\n",
    "- `model_name` 역시 변경하여 테스트가 가능합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "template = \"\"\"\n",
    "당신은 영어를 가르치는 10년차 영어 선생님입니다. 주어진 상황에 맞는 영어 회화를 작성해 주세요.\n",
    "양식은 [FORMAT]을 참고하여 작성해 주세요.\n",
    "\n",
    "#상황:\n",
    "{question}\n",
    "\n",
    "#FORMAT:\n",
    "- 영어 회화:\n",
    "- 한글 해석:\n",
    "\"\"\"\n",
    "\n",
    "# 프롬프트 템플릿을 이용하여 프롬프트를 생성합니다.\n",
    "prompt = PromptTemplate.from_template(template)\n",
    "\n",
    "# ChatOpenAI 챗모델을 초기화합니다.\n",
    "model = ChatOpenAI(model_name=\"gpt-4.1-nano\")\n",
    "\n",
    "# 문자열 출력 파서를 초기화합니다.\n",
    "output_parser = StrOutputParser()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 체인을 구성합니다.\n",
    "chain = prompt | model | output_parser"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 완성된 Chain을 실행하여 답변을 얻습니다.\n",
    "print(chain.invoke({\"question\": \"저는 식당에 가서 음식을 주문하고 싶어요\"}))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 완성된 Chain을 실행하여 답변을 얻습니다.\n",
    "# 스트리밍 출력을 위한 요청\n",
    "answer = chain.stream({\"question\": \"저는 식당에 가서 음식을 주문하고 싶어요\"})\n",
    "# 스트리밍 출력\n",
    "stream_response(answer)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 이번에는 question 을 '미국에서 피자 주문'으로 설정하여 실행합니다.\n",
    "# 스트리밍 출력을 위한 요청\n",
    "answer = chain.stream({\"question\": \"미국에서 피자 주문\"})\n",
    "# 스트리밍 출력\n",
    "stream_response(answer)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchain-kr-CdOel15G-py3.11",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
